/*
 *  ApplicationEngine.cpp
 *  SimpleWireframe
 *
 *  Created by BongjuKim on 11. 1. 30..
 *  Copyright 2011 thinkware. All rights reserved.
 *
 */

#ifdef PLATFORM_ANDROID
#include <android/log.h>

#include <Interfaces.hpp>
#include <ParametricEquations.hpp>
#else
#include <iostream>

#include "Interfaces.hpp"
#include "ParametricEquations.hpp"
#endif

using namespace std;

//namespace ParametricView
namespace SimpleWireframe
{

static const int SurfaceCount = 6;
static const int ButtonCount = SurfaceCount - 1;

struct Animation {
	bool Active;
	float Elapsed;
	float Duration;
	Visual StartingVisuals[SurfaceCount];
	Visual EndingVisuals[SurfaceCount];
};

class ApplicationEngine : public IApplicationEngine 
{
public:
	ApplicationEngine(IRenderingEngine* renderingEngine);
	~ApplicationEngine();
	void Initialize(int width, int height);
	void OnFingerUp(ivec2 location);
	void OnFingerDown(ivec2 location);
	void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
	void Render() const;
	void UpdateAnimation(float dt);
	
private:
	void PopulateVisuals(Visual* visuals) const;
	int  MapToButton(ivec2 touchpoint) const;
	vec3 MapToSphere(ivec2 touchpoint) const;
	float m_trackballRadius;
	ivec2 m_screenSize;
	ivec2 m_centerPoint;
	ivec2 m_fingerStart;
	bool m_spinning;
	Quaternion m_orientation;
	Quaternion m_previousOrientation;
	IRenderingEngine* m_renderingEngine;
	int m_currentSurface;
	ivec2 m_buttonSize;
	int m_pressedButton;
	int m_buttonSurfaces[ButtonCount];
	int m_width;
	int m_height;
	
	Animation m_animation;
};


IApplicationEngine* CreateApplicationEngine(IRenderingEngine* renderingEngine)
{
	return new ApplicationEngine(renderingEngine);
}


ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) : 
		m_spinning(false),
		m_renderingEngine(renderingEngine),
		m_pressedButton(-1)
{
	m_animation.Active = false;
	
	m_buttonSurfaces[0] = 0;
	m_buttonSurfaces[1] = 1;
	m_buttonSurfaces[2] = 2;
	m_buttonSurfaces[3] = 4;
	m_buttonSurfaces[4] = 5;
	m_currentSurface = 3;
	
	m_width = 320;
	m_height = 480;
}

ApplicationEngine::~ApplicationEngine()
{
	delete m_renderingEngine;
}

void ApplicationEngine::Initialize(int width, int height)
{
	m_width  = width;
	m_height = height;
	
	m_trackballRadius = width / 3;		 // 106.6
//	m_buttonSize = ivec2(4 * (height/10) / 3, height / 10);
	//
	// Customized Menu Button. Ratio 4:3
	//
	// if the display metrics is 320x480, the ratio is 320:480=1:R. Ratio=480/320=1.5
	// but, we have to truncate the height of bottom button layer. it's 48(480/10).
	// as a result, the display metrics 320x432. then the ratio is 320:432=1:$. Ratio=432/320=1.35
	// the width of the bottom button layer is calculated via producted the height of bottom button layer.
	// 48 * 1.35 = 64.8
	//	m_buttonSize.x = 4 * m_buttonSize.y / 3;  // 64 = 4 * 48 / 3
	//	m_buttonSize.x = m_buttonSize.y * ((height-m_buttonSize.y)/width);

	m_buttonSize.y = height / 10; // 48 = 480 / 10
	m_buttonSize.x = width/5;
	m_screenSize = ivec2(width, height - m_buttonSize.y); // 320 x 432 = 320 x (480 - 48)
	m_centerPoint = m_screenSize / 2;	 // 
	
	vector<ISurface*> surfaces(SurfaceCount);
	surfaces[0] = new Cone(3, 1);
	surfaces[1] = new Sphere(1.4f);
	surfaces[2] = new Torus(1.4f, 0.3f);
	surfaces[3] = new TrefoilKnot(1.8f);
	surfaces[4] = new KleinBottle(0.2f);
	surfaces[5] = new MobiusStrip(1);
	
	m_renderingEngine->Initialize(surfaces);
	for (int i = 0; i < SurfaceCount; i++) {
		delete surfaces[i];
	}
}

void ApplicationEngine::PopulateVisuals(Visual* visuals) const
{
	for (int buttonIndex = 0; buttonIndex < ButtonCount; buttonIndex++) {
		int visualIndex = m_buttonSurfaces[buttonIndex];
		visuals[visualIndex].Color = vec3(0.75f, 0.75f, 0.75f);
		if (m_pressedButton == buttonIndex) {
			visuals[visualIndex].Color = vec3(1, 1, 1);
		}
		
		visuals[visualIndex].ViewportSize = m_buttonSize;
		visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
		visuals[visualIndex].LowerLeft.y = 0;
		visuals[visualIndex].Orientation = Quaternion();
	}
	
//	visuals[m_currentSurface].LowerLeft = ivec2(0, 48);
//	visuals[m_currentSurface].ViewportSize = ivec2(320, 432);
	visuals[m_currentSurface].Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
	visuals[m_currentSurface].LowerLeft = ivec2(0, m_buttonSize.y);
	visuals[m_currentSurface].ViewportSize = ivec2(m_width, (m_height-m_buttonSize.y));
	visuals[m_currentSurface].Orientation = m_orientation;
}

void ApplicationEngine::Render() const
{
//	Visual visual;
//	visual.Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
//	visual.LowerLeft = ivec2(0, 48);
//	visual.ViewportSize = ivec2(320, 432);
//	visual.Orientation = m_orientation;
//	m_renderingEngine->Render(&visual);


//	vector<Visual> visual(1);
//	visual[0].Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
//	visual[0].LowerLeft = ivec2(0, 48);
//	visual[0].ViewportSize = ivec2(320, 432);
//	visual[0].Orientation = m_orientation;
//	m_renderingEngine->Render(visual);
//
#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "SimpleWireframe", "0. Render() : SurfaceCount(%d)", SurfaceCount);
#endif

	vector<Visual> visuals(SurfaceCount);

#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "SimpleWireframe", "1. Render() : m_animation.Active(%d)", m_animation.Active);
#endif

	if (!m_animation.Active) {

#ifdef PLATFORM_ANDROID
		__android_log_print(ANDROID_LOG_DEBUG, "SimpleWireframe", "2. Render() : m_animation.Active(%d)", m_animation.Active);
#endif
		PopulateVisuals(&visuals[0]);
	} else {
		float t = m_animation.Elapsed / m_animation.Duration;

#ifdef PLATFORM_ANDROID
		__android_log_print(ANDROID_LOG_DEBUG, "SimpleWireframe", "3. Render() : m_animation.Active(%d) t(%d) Elapsed(%d) Duration(%d)",
				m_animation.Active, t, m_animation.Elapsed, m_animation.Duration);
#endif


		for (int i = 0; i < SurfaceCount; i++) {
			const Visual& start = m_animation.StartingVisuals[i];
			const Visual& end = m_animation.EndingVisuals[i];
			Visual& tweened = visuals[i];

			tweened.Color = start.Color.Lerp(t, end.Color);
			tweened.LowerLeft = start.LowerLeft.Lerp(t, end.LowerLeft);
			tweened.ViewportSize = start.ViewportSize.Lerp(t, end.ViewportSize);
			tweened.Orientation = start.Orientation.Slerp(t, end.Orientation);
		}
	}

	m_renderingEngine->Render(visuals);
}


void ApplicationEngine::UpdateAnimation(float dt)
{
#ifndef PLATFORM_ANDROID
	std::cout << "UpdateAnimation:" << dt << "  Active:" << m_animation.Active << "  Elapsed:" << m_animation.Elapsed << endl;
#endif
	if (m_animation.Active) {
		m_animation.Elapsed += dt;
		if (m_animation.Elapsed > m_animation.Duration) {
			m_animation.Active = false;
		}
	}
}


void ApplicationEngine::OnFingerUp(ivec2 location)
{
	m_spinning = false;
	
#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "1. OnFingerUp:x(%d), y(%d)", location.x, location.y);
#endif

	if (m_pressedButton != -1 && m_pressedButton == MapToButton(location) && !m_animation.Active) 
	{
#ifdef PLATFORM_ANDROID
		__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "2. OnFingerUp:x(%d), y(%d)", location.x, location.y);
#endif
		m_animation.Active = true;
		m_animation.Elapsed = 0;
		m_animation.Duration = 0.25f;
		
		PopulateVisuals(&m_animation.StartingVisuals[0]);
		swap(m_buttonSurfaces[m_pressedButton], m_currentSurface);
		PopulateVisuals(&m_animation.EndingVisuals[0]);

#ifdef PLATFORM_ANDROID
		__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "3. OnFingerUp:Active(%d)", m_animation.Active);
#endif
	}
	
	m_pressedButton = -1;
}

void ApplicationEngine::OnFingerDown(ivec2 location)
{
	m_fingerStart = location;
	m_previousOrientation = m_orientation;
#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "OnFingerDown:x(%d), y(%d)", location.x, location.y);
#endif
	m_pressedButton = MapToButton(location);
	
	if (m_pressedButton == -1) {
		m_spinning = true;
	}
}


void ApplicationEngine::OnFingerMove(ivec2 oldLocation, ivec2 location)
{
	if (m_spinning) {
		vec3 start = MapToSphere(m_fingerStart);
		vec3 end = MapToSphere(location);
		Quaternion delta = Quaternion::CreateFromVectors(start, end);
		m_orientation = delta.Rotated(m_previousOrientation);
	}
	
	if (m_pressedButton != -1 && m_pressedButton != MapToButton(location)) {
		m_pressedButton = -1;
	}
}

int ApplicationEngine::MapToButton(ivec2 touchpoint) const
{
#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "1. MapToButton: touchpoint.x(%d), touchpoint.y(%d), m_screenSize.y(%d), m_buttonSize.y(%d)",
			touchpoint.x, touchpoint.y, m_screenSize.y, m_buttonSize.y);
#endif

	if (touchpoint.y < m_screenSize.y - m_buttonSize.y) {
		return -1;
	}

	
	int buttonIndex = touchpoint.x / m_buttonSize.x;
#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "2. touchpoint.x(%d) / m_buttonSize.x(%d) = buttonIndex(%d)...ButtonCount(%d)",
			touchpoint.x, m_buttonSize.x, buttonIndex, ButtonCount);
#endif
	if (buttonIndex >= ButtonCount) {
		return -1;
	}
#ifdef PLATFORM_ANDROID
	__android_log_print(ANDROID_LOG_DEBUG, "touch-event", "3. Done. buttonIndex(%d)", buttonIndex);
#endif

	return buttonIndex;
}

vec3 ApplicationEngine::MapToSphere(ivec2 touchpoint) const
{
	vec2 p = touchpoint - m_centerPoint;
	
	// Flip the y-axis because pixel coords increase toward the bottom.
	p.y = -p.y;
	
	const float radius = m_trackballRadius;
	const float safeRadius = radius - 1;
	
	if (p.Length() > safeRadius) {
		float theta = atan2(p.y, p.x);
		p.x = safeRadius * cos(theta);
		p.y = safeRadius * sin(theta);
	}
	
	float z = sqrt(radius * radius - p.LengthSquared());
	vec3 mapped = vec3(p.x, p.y, z);
	
	return mapped / radius;
}

}














